#include "oiltrans_sqlite.h"
#include "sqlite_orm/sqlite_orm.h"
#include <ctime>
#include <chrono>
#include <iostream>

namespace cores
{
    namespace database
    {
        namespace terminal
        {
            namespace sqlite
            {
                static OilTrans_Sqlite globalOilTrans("my_oil.sqlite");

                using namespace sqlite_orm;

                OilTrans_Sqlite::OilTrans_Sqlite(const std::string &db_name)
                    : OilTrans(), _dataBaseName(db_name)
                {
                }

                OilTrans_Sqlite::~OilTrans_Sqlite()
                {
                }

                inline std::string getYearMonthStr(const time_t &timestamp)
                {
                    std::string res{"000000"};
                    tm *time_info = localtime(&timestamp);

                    auto month = std::to_string(1 + time_info->tm_mon);
                    if (month.length() < 2)
                    {
                        month = "0" + month;
                    }

                    return std::to_string(1900 + time_info->tm_year) + month;
                };

                auto createStorage(const std::string &dbName, const uint32_t &time)
                {
                    auto yearMonthStr = getYearMonthStr(time);
                    auto table1name = std::string("transactions_") + yearMonthStr;
                    auto table2name = std::string("oil_tanks_") + yearMonthStr;
                    return make_storage(dbName,
                                        make_table(table1name,
                                                   make_column("trans_id", &OilTransaction::trans_id, unique()),
                                                   make_column("data_sn", &OilTransaction::data_sn),
                                                   make_column("station_code", &OilTransaction::station_code),
                                                   make_column("lora_id", &OilTransaction::lora_id),
                                                   make_column("nozzle_code", &OilTransaction::nozzle_code),
                                                   make_column("beg_time", &OilTransaction::beg_time),
                                                   make_column("end_time", &OilTransaction::end_time),
                                                   make_column("trans_beg_time", &OilTransaction::trans_beg_time),
                                                   make_column("trans_end_time", &OilTransaction::trans_end_time),
                                                   make_column("category", &OilTransaction::category),
                                                   make_column("volume", &OilTransaction::volume),
                                                   make_column("price", &OilTransaction::price),
                                                   make_column("amount", &OilTransaction::amount),
                                                   make_column("encoder_val", &OilTransaction::encoder_val),
                                                   make_column("create_time", &OilTransaction::create_time),
                                                   make_column("sort", &OilTransaction::sort),
                                                   make_column("type", &OilTransaction::type),
                                                   make_column("screen_raw_type", &OilTransaction::screen_raw_type),
                                                   make_column("screen_type", &OilTransaction::screen_type),
                                                   make_column("screen_raw_data", &OilTransaction::screen_raw_data),
                                                   primary_key(&OilTransaction::trans_id)),
                                        make_table(table2name,
                                                   make_column("trans_id", &LlqDataItem::trans_id, unique()),
                                                   make_column("code", &LlqDataItem::code),
                                                   make_column("category", &LlqDataItem::category),
                                                   make_column("oil_level", &LlqDataItem::oil_level),
                                                   make_column("water_level", &LlqDataItem::water_level),
                                                   make_column("temperature", &LlqDataItem::temperature),
                                                   make_column("volume", &LlqDataItem::volume),
                                                   make_column("oil_volume", &LlqDataItem::oil_volume),
                                                   make_column("water_volume", &LlqDataItem::water_volume),
                                                   make_column("create_time", &LlqDataItem::create_time),
                                                   make_column("sort", &LlqDataItem::sort),
                                                   make_column("type", &LlqDataItem::type),
                                                   primary_key(&LlqDataItem::trans_id)),
                                        make_table("serial_number",
                                                   make_column("lora_id", &SerialNumber::lora_id),
                                                   make_column("nozzle_code", &SerialNumber::nozzle_code),
                                                   make_column("trans_max_num", &SerialNumber::trans_max_num),
                                                   make_column("tax_max_num", &SerialNumber::tax_max_num)));
                }

                inline uint32_t getNowTime()
                {
                    auto now = std::chrono::system_clock::now();
                    auto duration = now.time_since_epoch();
                    return std::chrono::duration_cast<std::chrono::seconds>(duration).count();
                }

                std::vector<OilTransaction> OilTrans_Sqlite::getRawTransData(const bool &is_test)
                {
                    auto type = is_test ? 1 : 0;
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    return oil_storage_trans.get_all<OilTransaction>(where(c(&OilTransaction::sort) == 0 &&
                                                                           c(&OilTransaction::type) == type));
                }

                void OilTrans_Sqlite::addOneTransData(const OilTransaction &trans_item)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    oil_storage_trans.insert(trans_item);
                }

                void OilTrans_Sqlite::addOneTransData(const OilTransaction &trans_item, const uint16_t &serial_num)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    // 开启事务
                    oil_storage_trans.begin_transaction();

                    try
                    {
                        auto res = oil_storage_trans.get_all<OilTransaction>(where(c(&OilTransaction::beg_time) == trans_item.beg_time &&
                                                                                   c(&OilTransaction::data_sn) == trans_item.data_sn &&
                                                                                   c(&OilTransaction::lora_id) == trans_item.lora_id &&
                                                                                   c(&OilTransaction::nozzle_code) == trans_item.nozzle_code));
                        if (res.size() == 0)
                        {
                            // 如果不存在则，新增交易数据
                            oil_storage_trans.insert(trans_item);
                        }
                        // 更新最大流水号
                        oil_storage_trans.update_all(sqlite_orm::set(c(&SerialNumber::trans_max_num) = serial_num),
                                                     where(is_equal(&SerialNumber::lora_id, trans_item.lora_id) && is_equal(&SerialNumber::nozzle_code, trans_item.nozzle_code)));

                        // 提交事务
                        oil_storage_trans.commit();
                    }
                    catch (const std::system_error &e)
                    {
                        // 回滚事务
                        oil_storage_trans.rollback();
                        throw std::system_error(errno, std::system_category(), "add new transaction data error...");
                    }
                }

                void OilTrans_Sqlite::updateTransDataStatus(const std::vector<std::string> &ids, const bool &is_upload)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();

                    uint8_t sort_val = is_upload ? 1 : 0;
                    oil_storage_trans.update_all(set(c(&OilTransaction::sort) = sort_val),
                                                 where(in(&OilTransaction::trans_id, ids)));
                }

                std::vector<SerialNumber> OilTrans_Sqlite::getSerialNumberData()
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    return oil_storage_trans.get_all<SerialNumber>();
                }

                std::vector<SerialNumber> OilTrans_Sqlite::getSerialNumberData(
                    const uint16_t &lora_id,
                    const uint8_t &nozzle_code)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    return oil_storage_trans.get_all<SerialNumber>(where(is_equal(&SerialNumber::lora_id, lora_id) &&
                                                                         is_equal(&SerialNumber::nozzle_code, nozzle_code)));
                }

                void OilTrans_Sqlite::addSerialNumberRecord(const SerialNumber &item)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    oil_storage_trans.insert(item);
                }

                void OilTrans_Sqlite::updateSerialNumberRecord(const SerialNumber &item)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    oil_storage_trans.update_all(set(c(&SerialNumber::trans_max_num) = item.trans_max_num,
                                                     c(&SerialNumber::tax_max_num) = item.tax_max_num),
                                                 where(c(&SerialNumber::lora_id) == item.lora_id &&
                                                       c(&SerialNumber::nozzle_code) == item.nozzle_code));
                }

                std::vector<LlqDataItem> OilTrans_Sqlite::getNoUploadLlqData(const bool &is_test)
                {
                    uint8_t type = is_test ? 1 : 0;
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    auto result = oil_storage_trans.get_all<LlqDataItem>(where(c(&LlqDataItem::sort) == 0 &&
                                                                               c(&LlqDataItem::type) == type));
                    return result;
                }

                void OilTrans_Sqlite::addNewLlqData(const LlqDataItem &item)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    oil_storage_trans.insert(item);
                }

                void OilTrans_Sqlite::updateLlqDataStatus(const std::vector<std::string> &ids, const bool &is_upload)
                {
                    auto oil_storage_trans = createStorage(_dataBaseName, getNowTime());
                    oil_storage_trans.sync_schema();
                    uint8_t sort_val = is_upload ? 1 : 0;
                    oil_storage_trans.update_all(set(c(&LlqDataItem::sort) = sort_val),
                                                 where(in(&LlqDataItem::trans_id, ids)));
                }
            }
        }
    }
}